import type { DialogProps, DrawerProps } from 'element-plus';
import {
  type Component,
  type ComponentInternalInstance,
  createVNode,
  h,
  render,
  getCurrentInstance,
  onBeforeUnmount,
  resolveComponent,
  nextTick
} from 'vue';
import FloatComponents from '@/components/floatComponents/index.vue';

type GlobalLayerOptions<T = any, P = any> = {
  /** 透传参数*/
  props?: P;
  solts?: Record<string, any>;
  [key: string]: any;
} & {
  [key in keyof Partial<Omit<T, 'props' | 'solts'>>]: T[key];
};

let currentIndex = 1;

type useFloatIns<T = any, P = any> = {
  open(arg: GlobalLayerOptions<T, P>): Promise<void>;
  close(): Promise<void>;
};

function useFloat(
  type: 'dialog',
  component: Component | string
): useFloatIns<DialogProps>;
function useFloat(
  type: 'drawer',
  component: Component | string
): useFloatIns<DrawerProps>;
function useFloat(
  type: 'page',
  component: Component | string
): useFloatIns<any>;
function useFloat(type = 'page', component: Component | string) {
  const currentInstanceStack: ComponentInternalInstance[] = [];

  const ctx = getCurrentInstance();

  const renderNode =
    typeof component == 'string' ? resolveComponent(component) : component;
  const close = () =>
    new Promise((resolve) => {
      const instance = currentInstanceStack.pop();
      instance!.exposed!.close(resolve);
    });

  const open = async (config: GlobalLayerOptions) => {
    let container: HTMLDivElement | undefined = document.createElement('div');
    const { props, ...wapperProps } = config;
    const renderWrapper = createVNode(
      FloatComponents,
      {
        type,
        onClosed: (closeEventHandle?: () => void) => {
          currentIndex--;
          render(null, container!);
          container = undefined;
          if (closeEventHandle) closeEventHandle();
        },
        zindex: currentIndex++,
        ...wapperProps
      },
      {
        default: () => h(renderNode, Object.assign({}, props)),
        ...config.solts
      }
    );
    const parentRoot =
      type == 'page'
        ? document.getElementById('layout-content')!
        : document.body;
    renderWrapper.appContext = ctx!.appContext;
    render(renderWrapper, container);
    const instance = renderWrapper.component;
    await nextTick();
    parentRoot.appendChild(container.firstElementChild!);
    if (!instance) return Promise.reject();
    instance.exposed!.open();
    currentInstanceStack.push(instance);
    const stopWatch = instance.proxy!.$watch('$route', () => {
      instance.exposed!.close();
      stopWatch();
    });
    return Promise.resolve();
  };

  onBeforeUnmount(() => {
    while (currentInstanceStack.length) {
      const target = currentInstanceStack.pop();
      target?.exposed?.close();
    }
  });
  return { open, close };
}

export default useFloat;
